#include <iostream>
#include <fstream>
#include <iomanip>
#include <unistd.h>
#include <sstream>
#include <assert.h>

#include "nature.hpp"
#include "grass.hpp"
#include "sheep.hpp"
#include "wolf.hpp"

Nature *Nature::get_instance()
{
    static Nature *g_instance = nullptr;

    if (nullptr == g_instance)
    {
        g_instance = new Nature;
    }

    return g_instance;
}

void Nature::init(int w, int s, int g)
{
    // TODO 合并一下复制粘贴的代码
    for (int i = 0; i < w; ++i)
        _random_add(new Wolf());

    for (int i = 0; i < s; ++i)
        _random_add(new Sheep());

    for (int i = 0; i < g; ++i)
        _random_add(new Grass());
}

void Nature::run()
{
    for (auto c : m_creatures)
    {
        Event *e = c->run();

        if (e != nullptr)
            m_events.push_back(e);
    }

    ++m_round;
}

void Nature::report()
{
    std::cout << "当前是第 " << m_round << " 回合" << std::endl;
    for (auto e : m_events)
    {
        e->print();
    }
}

void Nature::print()
{
    // 每个坐标最长打印 (xx@ yy$ zz#)
    static const char *sapce = "(     -     ) ";

    std::cout << "当前是第 " << m_round << " 回合" << std::endl;
    // 打印每一行和每一列
    for (int i = 0; i < m_size; ++i)
    {
        for (int j = 0; j < m_size; ++j)
        {
            std::set<Creature *> *p_set = m_map.get_objects(Position(i, j));

            if (nullptr != p_set)
            {
                int w = 0, s = 0, g = 0;
                // TODO 低效的
                for (auto c : *p_set)
                {
                    switch (c->type())
                    {
                    case CT_WOLF:
                        ++w;
                        break;
                    case CT_SHEEP:
                        ++s;
                        break;
                    case CT_GRASS:
                        ++g;
                        break;
                    default:
                        break;
                    }
                }
                std::ostringstream oss;
                oss << "(";
                if (w > 0)
                    oss << std::setw(2) << std::setfill(' ') << w << std::setw(0) << "@ ";
                else
                    oss << "    ";

                if (s > 0)
                    oss << std::setw(2) << std::setfill(' ') << s << std::setw(0) << "$ ";
                else
                    oss << "    ";

                if (g > 0)
                    oss << std::setw(2) << std::setfill(' ') << g << std::setw(0) << "#";
                else
                    oss << "   ";
                oss << ") ";

                std::cout
                    << oss.str();
            }
            else
            {
                std::cout << sapce;
            }
        }
        std::cout << std::endl; // 换行
    }
}

void Nature::_random_add(Creature *c)
{
    Position p;

    if (m_map.random_space(p))
    {
        c->move_to(p);
        m_creatures.insert(c);
    }
}

void Nature::move_to(Creature *c, const Position &p)
{
    auto sets = this->get_creature(c->position());

    if (nullptr != sets)
    {
        sets->erase(c);
    }

    this->put_creature(c, p);
}

void Nature::save(const char *file)
{
    std::ofstream ofs(file, std::ios::binary);

    if (!ofs.is_open())
    {
        std::cout << "open file " << file << " failed." << std::endl;
        return;
    }

    // 1. 保存所有的生物, 二进制数据格式如下
    // 生物总数 [生物类型 数据长度 数据] [...] ... // 方括号内表示一条数据，可以存N条
    size_t size = m_creatures.size();

    ofs.write((const char *)&size, sizeof(size));
    for (auto item : m_creatures)
    {
        std::string tmp;
        item->to_string(tmp);

        size_t tmp_len = tmp.length();

        if (tmp_len > 0)
        {
            int tmp_type = (int)item->type();
            ofs.write((const char *)&tmp_type, sizeof(tmp_type));

            ofs.write((const char *)&tmp_len, sizeof(tmp_len));
            ofs.write(tmp.data(), tmp.length());
        }
    }
    // 2. 保存所有的事件
    size = 0; // m_events.size(); // TODO 先固定写0后续补充
    ofs.write((const char *)&size, sizeof(size));

    ofs.close();
}

void Nature::load(const char *file)
{
    // 1. 先清除已有数据
    m_map.clear();
    for (auto item : m_creatures)
    {
        delete item;
    }
    m_creatures.clear();

    // 2. 读数据文件并依次添加
    std::ifstream ifs(file, std::ios::binary);
    if (!ifs.is_open())
    {
        std::cout << "打开数据文件" << file << "失败" << std::endl;
        return;
    }

    size_t size = 0;

    ifs.read((char *)&size, sizeof(size));
    if (ifs.fail())
    {
        std::cout << "读文件失败1" << std::endl;
        return;
    }

    for (size_t i = 0; i < size; ++i)
    {
        int tmp_type = 0;
        ifs.read((char *)&tmp_type, sizeof(tmp_type));
        if (ifs.fail())
        {
            std::cout << "读文件失败2" << std::endl;
            return;
        }

        size_t tmp_len = 0;
        ifs.read((char *)&tmp_len, sizeof(tmp_len));
        if (ifs.fail())
        {
            std::cout << "读文件失败2" << std::endl;
            return;
        }

        assert(tmp_len < 1024);
        char *buf = new char[tmp_len];
        ifs.read(buf, tmp_len);

        std::string tmp;
        tmp.assign(buf, tmp_len);

        Creature *c = nullptr;

        switch (tmp_type)
        {
        case (int)CT_WOLF:
            c = new Wolf();
            break;

        case (int)CT_SHEEP:
            c = new Sheep();
            break;

        case (int)CT_GRASS:
            c = new Grass();
            break;

        default:
            std::cout << "错误的数据" << std::endl;
            return;
        }

        c->from_string(tmp);

        this->m_creatures.insert(c);
        this->m_map.put_object(c, c->position());
    }
}